

#import "GameNode.h"
#import "Bike.h"
#import "BikeContactListener.h"
#import "Box2D.h"
#import "Box2DHelper.h"
#define MOTOR_SPEED (10)

#define use_wheel

@interface Bike()
- (void) createBox2DBody;
@end

@implementation Bike

@synthesize game = _game;
@synthesize sprite = _sprite;
@synthesize awake = _awake;
@synthesize diving = _diving;
#pragma mark -
+ (id) bikeWithGame:(GameNode*)game {
	return [[[self alloc] initWithGame:game] autorelease];
}

- (id) initWithGame:(GameNode*)game {
	
//	if ((self = [super init])) {
    if ((self = [super initWithFile:@"bike.png"])) {


		self.game = game;
		
#ifndef DRAW_BOX2D_WORLD
//        [self setTexture:[[CCTextureCache sharedTextureCache] addImage:@"resetButton.png"]];
#endif
		body_ = NULL;
		_radius = 14.0f;

		_contactListener = new BikeContactListener(self);
		_game.world->SetContactListener(_contactListener);

		[self reset];
	}
	return self;
}

- (void) dealloc {

	self.game = nil;
	
#ifndef DRAW_BOX2D_WORLD
	self.sprite = nil;
#endif

	delete _contactListener;
	[super dealloc];
}

- (void) reset {
	_flying = NO;
	_diving = NO;
	_nPerfectSlides = 0;
	if (body_) {
		_game.world->DestroyBody(body_);
	}
    #ifdef use_wheel
    if (wheel1Node_.body) {
        _game.world->DestroyBody(wheel1_);
        _game.world->DestroyBody(wheel2_);
         _game.world->DestroyBody(axle1_);
         _game.world->DestroyBody(axle2_);
        [_game.terrain removeChild:wheel1Node_ cleanup:YES];
        [_game.terrain removeChild:wheel2Node_ cleanup:YES];

        
    }
#endif
	[self createBox2DBody];
#ifdef use_wheel
    [self createWheel];
#endif
	[self updateNode];
	[self sleep];
}
- (void) createWheel {
    //
    // Cart
    //
    // Reuses the already created body
    b2Body *cart =body_;
    b2FixtureDef fd;
    b2PolygonShape polyShape;
    b2BodyDef bodyDef;
    
    // The cart is composed of 2 fixtures:
    // A box (lower part)
    // A 5-vertices poligon (upper part)
    
    // Create lower part of the cart
    polyShape.SetAsBox(1.0f, 0.25f);
    fd.shape = &polyShape;
    fd.friction		= 0.5f;
    fd.density		= 3;
    fd.restitution	= 0.1f;
    fd.filter.groupIndex = 0;
    cart->CreateFixture(&fd);
    
    // Create upper part of the cart
    //row 1, col 1
    int num = 6;
    b2Vec2 verts[] = {
        b2Vec2(-30.6f / PTM_RATIO, -14.6f / PTM_RATIO),
        b2Vec2(30.4f / PTM_RATIO, -14.6f / PTM_RATIO),
        b2Vec2(30.6f / PTM_RATIO, -0.1f / PTM_RATIO),
        b2Vec2(20.0f / PTM_RATIO, 0.8f / PTM_RATIO),
        b2Vec2(15.9f / PTM_RATIO, 13.3f / PTM_RATIO),
        b2Vec2(-30.4f / PTM_RATIO, 14.1f / PTM_RATIO)
    };

    polyShape.Set(verts,num);
    fd.filter.groupIndex = 0;
    cart->CreateFixture(&fd);
	
    cart->SetType(b2_dynamicBody);
    
    //
    // Axles
    //
    // Axle 1
    bodyDef.position = cart->GetWorldCenter();
    bodyDef.type = b2_dynamicBody;
    axle1_ = _game.world->CreateBody(&bodyDef);
    
    polyShape.SetAsBox(1.05f, 0.05f, b2Vec2(-0.6f, -0.3f), 0 );
    fd.shape = &polyShape;
    fd.density	= 1;
    fd.filter.groupIndex = 0;
    axle1_->CreateFixture(&fd);
    
    // Axle 2
    axle2_ = _game.world->CreateBody(&bodyDef);
    polyShape.SetAsBox(0.05f, 1.05f, b2Vec2(0.3f, -0.3f), 0 );
    axle2_->CreateFixture(&fd);
    
    //
    // Suspensions
    //
    b2PrismaticJointDef prismaticDef;
    prismaticDef.Initialize(cart, axle1_, axle1_->GetWorldCenter() , b2Vec2(0, 1));
    prismaticDef.lowerTranslation = -0.001f;
    prismaticDef.upperTranslation = 0.001f;
    prismaticDef.enableLimit = true;
    spring1_ = (b2PrismaticJoint*)_game.world->CreateJoint(&prismaticDef);
    
    // Front Prismatic Joint
    prismaticDef.Initialize(cart, axle2_, axle2_->GetWorldCenter(), b2Vec2(0, 1));
    prismaticDef.lowerTranslation = -0.001f;
    prismaticDef.upperTranslation = 0.001f;
    prismaticDef.enableLimit = true;
    spring2_ = (b2PrismaticJoint*)_game.world->CreateJoint(&prismaticDef);
    
    
    //
    // Wheels
    //
    
    bodyDef.position = axle1_->GetWorldCenter();
    wheel1_ = _game.world->CreateBody(&bodyDef);
    
    b2CircleShape	circleShape;
    circleShape.m_radius = 0.25f;
    fd.friction		= 0.9f;
    fd.density		= 1;
    fd.restitution	= 0.1f;
    fd.shape = &circleShape;
    fd.filter.groupIndex = 0;
    wheel1_->CreateFixture(&fd);
    
    // Front Wheel (Wheel 2)
    bodyDef.position = axle2_->GetWorldCenter();
    wheel2_ = _game.world->CreateBody(&bodyDef);
    wheel2_->CreateFixture(&fd);
    
    //
    // Motors
    //
    b2RevoluteJointDef revoluteDef;
    revoluteDef.Initialize(wheel1_, axle1_, wheel1_->GetWorldCenter() );
    motor1_ = (b2RevoluteJoint*) _game.world->CreateJoint(&revoluteDef);
    
    revoluteDef.Initialize(wheel2_, axle2_, wheel2_->GetWorldCenter() );
    motor2_ = (b2RevoluteJoint*) _game.world->CreateJoint(&revoluteDef);
    
    //
    // Update motor
    //
    //		[self scheduleUpdate]; // already scheduled by base class, no need to re-schedule
    
    // calculate base spring force from the mass of the cart//
    baseSpringForce_ = cart->GetMass() * 5;
    
    
    //
    // Movement & Jump
//    jumpImpulse_ = JUMP_IMPULSE;
    
    //
    // Sprites & BodyNodes
    //
    // Wheel 1
    wheel1Node_ = [[WheelNode alloc] initWithBody:wheel1_ game:_game];
    [_game.terrain addChild:wheel1Node_ z:1];
    [wheel1Node_ release];
    
    // Wheel 2
    wheel2Node_ = [[WheelNode alloc] initWithBody:wheel2_ game:_game];
    [_game.terrain addChild:wheel2Node_ z:1];
    [wheel2Node_ release];

}

- (void) createBox2DBody {
	
	b2BodyDef bd;
	bd.type = b2_dynamicBody;
	bd.linearDamping = 0.05f;
	bd.fixedRotation = true;
	
	// start position
	CGPoint p = ccp(0, _game.screenH/2+_radius);
	CCLOG(@"start position = %f, %f", p.x, p.y);

	bd.position.Set(p.x * [Box2DHelper metersPerPoint], p.y * [Box2DHelper metersPerPoint]);
	body_ = _game.world->CreateBody(&bd);
	
	b2CircleShape shape;
	shape.m_radius = _radius * [Box2DHelper metersPerPoint];
	
	b2FixtureDef fd;
	fd.shape = &shape;
	fd.density = 1.0f;
	fd.restitution = 0; // bounce
	fd.friction = 0;
	
	body_->CreateFixture(&fd);
}

- (void) sleep {
    [self wake:NO];
}
- (void) wake:(BOOL) wake{
    _awake = wake;
	body_->SetActive(wake);
#ifdef use_wheel
    wheel1_->SetActive(wake);
    wheel2_->SetActive(wake);
    axle1_->SetActive(wake);
    axle2_->SetActive(wake);
#endif

}
- (void) wake {
[self wake:YES];
	body_->ApplyLinearImpulse(b2Vec2(1,2), body_->GetPosition());
}

- (void) updatePhysics {
//    NSLog(@"Bike ---updatePhysics---- ");
	// apply force if diving
	if (_diving) {
		if (!_awake) {
			[self wake];
			_diving = NO;
		} else {
			body_->ApplyForce(b2Vec2(0,-40),body_->GetPosition());
		}
	}
	
	// limit velocity
	const float minVelocityX = 3;
	const float minVelocityY = -40;
	b2Vec2 vel = body_->GetLinearVelocity();
	if (vel.x < minVelocityX) {
		vel.x = minVelocityX;
	}
	if (vel.y < minVelocityY) {
		vel.y = minVelocityY;
	}
	body_->SetLinearVelocity(vel);
}

- (void) updateNode {
//	NSLog(@"Bike ---updateNode---- ");
	CGPoint p;
	p.x = body_->GetPosition().x * [Box2DHelper pointsPerMeter];
	p.y = body_->GetPosition().y * [Box2DHelper pointsPerMeter];
	
	// CCNode position and rotation
	self.position = p;
	b2Vec2 vel = body_->GetLinearVelocity();
	float angle = atan2f(vel.y, vel.x);

#ifdef DRAW_BOX2D_WORLD
	body_->SetTransform(body_->GetPosition(), angle);
#else
	self.rotation = -1 * CC_RADIANS_TO_DEGREES(angle);
#endif
	
	// collision detection
	b2Contact *c = _game.world->GetContactList();
	if (c) {
		if (_flying) {
			[self landed];
		}
	} else {
		if (!_flying) {
			[self tookOff];
		}
	}
	
	// TEMP: sleep if below the screen
	if (p.y < -_radius && _awake) {
		[self sleep];
	}
    #ifdef use_wheel
    //车体晃动
	spring1_->SetMaxMotorForce( baseSpringForce_ + (40 * baseSpringForce_ * powf(spring1_->GetJointTranslation(), 2) ) );
	spring1_->SetMotorSpeed( -20 * spring1_->GetJointTranslation() );
	
	spring2_->SetMaxMotorForce( baseSpringForce_ + (40 * baseSpringForce_ * powf(spring2_->GetJointTranslation(), 2) ) );
	spring2_->SetMotorSpeed( -20 * spring2_->GetJointTranslation() );
    
    //旋转车轮
    motor1_->EnableMotor(true);
    motor1_->SetMotorSpeed((float32) (MOTOR_SPEED* M_PI * 0.5));
    motor1_->SetMaxMotorTorque( 10 );
    
    motor2_->EnableMotor(true);
    motor2_->SetMotorSpeed((float32) (MOTOR_SPEED* M_PI * 0.5));
    motor2_->SetMaxMotorTorque( 10 );
    
    [wheel1Node_ update:0];
    [wheel2Node_ update:0];
#endif
}

- (void) landed {
//	CCLOG(@"landed");
	_flying = NO;
}

- (void) tookOff {
//	CCLOG(@"tookOff");
	_flying = YES;
	b2Vec2 vel = body_->GetLinearVelocity();
//	CCLOG(@"vel.y = %f",vel.y);
	if (vel.y > kPerfectTakeOffVelocityY) {
//		CCLOG(@"perfect slide");
		_nPerfectSlides++;
		if (_nPerfectSlides > 1) {
			if (_nPerfectSlides == 4) {
				[_game showFrenzy];
			} else {
				[_game showPerfectSlide];
			}
		}
	}
}

- (void) hit {
//	CCLOG(@"hit");
	_nPerfectSlides = 0;
	[_game showHit];
}

- (void) setDiving:(BOOL)diving {
	if (_diving != diving) {
		_diving = diving;
		// TODO: change sprite image here
	}
}

@end

#pragma mark -
#pragma mark WheelNode

@implementation WheelNode
-(id) initWithBody:(b2Body*)body game:(GameNode*)game{
    if (self=[super initWithBody:body game:game image:@"wheel.png"]) {
//        [self scheduleUpdate];
    }
    return self;
}
-(void) update:(ccTime)dt
{
//    NSLog(@"WheelNode --update---");
    b2Vec2 bPos = body_->GetPosition();
//    (p.x * [Box2DHelper metersPerPoint], p.y * [Box2DHelper metersPerPoint])
    self.position = ccp( bPos.x * [Box2DHelper pointsPerMeter], bPos.y * [Box2DHelper pointsPerMeter] );

}


@end
